package cn.turboinfo.fuyang.api.domain.mini.usecase.index;

import cn.turboinfo.fuyang.api.domain.common.handler.staff.StaffDataFactory;
import cn.turboinfo.fuyang.api.domain.common.service.division.DivisionService;
import cn.turboinfo.fuyang.api.domain.common.service.shop.HousekeepingShopService;
import cn.turboinfo.fuyang.api.domain.common.service.staff.HousekeepingStaffService;
import cn.turboinfo.fuyang.api.domain.common.usecase.AbstractUseCase;
import cn.turboinfo.fuyang.api.entity.common.enumeration.staff.StaffStatus;
import cn.turboinfo.fuyang.api.entity.common.fo.staff.ViewStaffFO;
import cn.turboinfo.fuyang.api.entity.common.pojo.division.Division;
import cn.turboinfo.fuyang.api.entity.common.pojo.shop.HousekeepingShop;
import cn.turboinfo.fuyang.api.entity.common.pojo.staff.HousekeepingStaff;
import cn.turboinfo.fuyang.api.entity.mini.fo.index.MiniIndexNearbyStaff;
import kotlin.Pair;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import net.sunshow.toolkit.core.qbean.helper.component.mapper.BeanMapper;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 列表首页附近服务人员
 *
 * @author sunshow
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class MiniIndexListNearbyStaffUseCase extends AbstractUseCase<MiniIndexListNearbyStaffUseCase.InputData, MiniIndexListNearbyStaffUseCase.OutputData> {

    private final HousekeepingShopService housekeepingShopService;

    private final HousekeepingStaffService housekeepingStaffService;

    private final DivisionService divisionService;

    private final StaffDataFactory staffDataFactory;

    @Override
    protected OutputData doAction(InputData inputData) {
        BigDecimal longitude = inputData.getLongitude();
        BigDecimal latitude = inputData.getLatitude();

        List<HousekeepingShop> shopList = new ArrayList<>();

        // 距离
        Map<Long, BigDecimal> shopIdDistanceMap;

        if (longitude != null && latitude != null) {
            // 获取距离10公里内最近的10家门店
            List<Pair<HousekeepingShop, BigDecimal>> pairList = housekeepingShopService.findByGeoRadius(longitude, latitude, 10D, 10L);
            shopList = pairList.stream()
                    .map(Pair::getFirst)
                    .collect(Collectors.toList());
            shopIdDistanceMap = pairList.stream()
                    .collect(
                            Collectors.toMap(
                                    pair -> pair.getFirst().getId(),
                                    Pair::getSecond
                            )
                    );
        } else {
            shopIdDistanceMap = new HashMap<>();
        }

        // 如何门店列表是空 则不按地区去查询
        if (shopList.isEmpty()) {
            // 否则取评分最高的20家门店
            shopList = housekeepingShopService.findTopByCreditScore(20);
        }

        // 根据门店取对应公司
        Set<Long> companyIdSet = shopList.stream()
                .map(HousekeepingShop::getCompanyId)
                .collect(Collectors.toSet());

        // TODO 这里简单粗暴全部查出来分组取了 量大需要优化
        List<HousekeepingStaff> housekeepingStaffList = housekeepingStaffService.findByCompanyIdCollection(companyIdSet)
                .stream()
                .filter(it -> it.getStatus().equals(StaffStatus.PUBLISHED))
                .toList();

        // 拼装企业信息
        staffDataFactory.assembleCompany(housekeepingStaffList);

        Map<Long, List<HousekeepingStaff>> companyIdStaffListMap = housekeepingStaffList.stream()
                .collect(
                        Collectors.groupingBy(
                                HousekeepingStaff::getCompanyId,
                                Collectors.toList()
                        )
                );

        // 组装返回数据
        List<MiniIndexNearbyStaff> nearbyStaffList = shopList.stream()
                .filter(shop -> companyIdStaffListMap.containsKey(shop.getCompanyId()))
                .map(shop -> {
                    List<HousekeepingStaff> staffList = companyIdStaffListMap.get(shop.getCompanyId())
                            .stream()
                            .sorted(Comparator.comparing(HousekeepingStaff::getCreditScore).reversed())
                            .limit(5)
                            .toList();

                    MiniIndexNearbyStaff nearbyStaff = new MiniIndexNearbyStaff();

                    Set<Long> provinceCodeSet = staffList.stream()
                            .map(HousekeepingStaff::getProvinceCode)
                            .collect(Collectors.toSet());
                    val divisionMap = divisionService.findByIdCollection(provinceCodeSet).stream().collect(Collectors.toMap(Division::getId, Function.identity()));

                    List<ViewStaffFO> sortedStaffList = staffList.stream()
                            .sorted(Comparator.comparing(HousekeepingStaff::getCreditScore).reversed())
                            .limit(5)
                            .map(it -> {
                                ViewStaffFO ViewStaffFO = BeanMapper.map(it, ViewStaffFO.class);

                                if (it.getDisplayName().isBlank()) {
                                    ViewStaffFO.setDisplayName(it.getName());
                                }

                                if (divisionMap.containsKey(it.getProvinceCode())) {
                                    ViewStaffFO.setProvinceName(divisionMap.get(it.getProvinceCode()).getAreaName());
                                }

                                return ViewStaffFO;

                            })
                            .toList();
                    // random get one from sortedStaffList
                    ViewStaffFO staff = sortedStaffList.get(new Random().nextInt(sortedStaffList.size()));

                    nearbyStaff.setStaff(staff);
                    nearbyStaff.setDistance(shopIdDistanceMap.get(shop.getId()));
                    return nearbyStaff;
                })
                .limit(10)
                .toList();

        return OutputData.builder()
                .nearbyStaffList(nearbyStaffList)
                .build();
    }

    @EqualsAndHashCode(callSuper = true)
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public static class InputData extends AbstractUseCase.InputData {

        /**
         * 经度
         */
        private BigDecimal longitude;

        /**
         * 纬度
         */
        private BigDecimal latitude;

    }

    @Getter
    @Setter
    @Builder
    public static class OutputData extends AbstractUseCase.OutputData {

        /**
         * 附近服务人员
         */
        private List<MiniIndexNearbyStaff> nearbyStaffList;

    }
}
